home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 21 / Cream of the Crop 21 (Terry Blount) (October 1996).iso / os2 / e33el2.zip / emacs / 19.33 / lisp / hanoi.el < prev    next >
Lisp/Scheme  |  1996-02-27  |  7KB  |  228 lines

  1. ;;; hanoi.el --- towers of hanoi in GNUmacs
  2.  
  3. ;; Author: Damon Anton Permezel
  4. ;; Maintainer: FSF
  5. ;; Keywords: games
  6.  
  7. ; Author (a) 1985, Damon Anton Permezel
  8. ; This is in the public domain
  9. ; since he distributed it without copyright notice in 1985.
  10.  
  11. ;;; Commentary:
  12.  
  13. ;; Solves the Towers of Hanoi puzzle while-U-wait.
  14. ;;
  15. ;; The puzzle: Start with N rings, decreasing in sizes from bottom to
  16. ;; top, stacked around a post.  There are two other posts.  Your mission,
  17. ;; should you choose to accept it, is to shift the pile, stacked in its
  18. ;; original order, to another post.
  19. ;;
  20. ;; The challenge is to do it in the fewest possible moves.  Each move
  21. ;; shifts one ring to a different post.  But there's a rule; you can
  22. ;; only stack a ring on top of a larger one.
  23. ;;
  24. ;; The simplest nontrivial version of this puzzle is N = 3.  Solution
  25. ;; time rises as 2**N, and programs to solve it have long been considered
  26. ;; classic introductory exercises in the use of recursion.
  27. ;;
  28. ;; The puzzle is called `Towers of Hanoi' because an early popular
  29. ;; presentation wove a fanciful legend around it.  According to this
  30. ;; myth (uttered long before the Vietnam War), there is a Buddhist
  31. ;; monastery at Hanoi which contains a large room with three time-worn
  32. ;; posts in it surrounded by 21 golden discs.  Monks, acting out the
  33. ;; command of an ancient prophecy, have been moving these disks, in
  34. ;; accordance with the rules of the puzzle, once every day since the
  35. ;; monastery was founded over a thousand years ago.  They are said
  36. ;; believe that when the last move of the puzzle is completed, the
  37. ;; world will end in a clap of thunder.  Fortunately, they are nowhere
  38. ;; even close to being done...
  39.  
  40. ;;; Code:
  41.  
  42. ;;;
  43. ;;; hanoi-topos - direct cursor addressing
  44. ;;;
  45. (defun hanoi-topos (row col)
  46.   (goto-line row)
  47.   (beginning-of-line)
  48.   (forward-char col))
  49.  
  50. ;;;
  51. ;;; hanoi - user callable Towers of Hanoi
  52. ;;;
  53. ;;;###autoload
  54. (defun hanoi (nrings)
  55.   "Towers of Hanoi diversion.  Argument is number of rings."
  56.   (interactive "p")
  57.   (if (<= nrings 1) (setq nrings 7))
  58.   (let* (floor-row
  59.      fly-row
  60.      (window-height (1- (window-height (selected-window))))
  61.      (window-width (window-width (selected-window)))
  62.  
  63.      ;; This is half the spacing to use between poles.
  64.      (pole-spacing (/ window-width 6)))
  65.     (if (not (and (> window-height (1+ nrings))
  66.           (> pole-spacing nrings)))
  67.     (progn
  68.       (delete-other-windows)
  69.       (if (not (and (> (setq window-height
  70.                  (1- (window-height (selected-window))))
  71.                (1+ nrings))
  72.             (> (setq pole-spacing (/ window-width 6))
  73.                nrings)))
  74.           (error "Window is too small (need at least %dx%d)"
  75.              (* 6 (1+ nrings)) (+ 2 nrings)))))
  76.     (setq floor-row (if (> (- window-height 3) (1+ nrings))
  77.             (- window-height 3) window-height))
  78.     (let ((fly-row (- floor-row nrings 1))
  79.       ;; pole: column . fill height
  80.       (pole-1 (cons (1- pole-spacing) floor-row))
  81.       (pole-2 (cons (1- (* 3 pole-spacing)) floor-row))
  82.       (pole-3 (cons (1- (* 5 pole-spacing)) floor-row))
  83.       (rings (make-vector nrings nil)))
  84.       ;; construct the ring list
  85.       (let ((i 0))
  86.     (while (< i nrings)
  87.       ;; ring: [pole-number string empty-string]
  88.       (aset rings i (vector nil
  89.                 (make-string (+ i i 3) (+ ?0 (% i 10)))
  90.                 (make-string (+ i i 3) ?\  )))
  91.       (setq i (1+ i))))
  92.       ;;
  93.       ;; init the screen
  94.       ;;
  95.       (switch-to-buffer "*Hanoi*")
  96.       (setq buffer-read-only nil)
  97.       (buffer-disable-undo (current-buffer))
  98.       (erase-buffer)
  99.       (let ((i 0))
  100.     (while (< i floor-row)
  101.       (setq i (1+ i))
  102.       (insert-char ?\  (1- window-width))
  103.       (insert ?\n)))
  104.       (insert-char ?= (1- window-width))
  105.  
  106.       (let ((n 1))
  107.     (while (< n 6)
  108.       (hanoi-topos fly-row (1- (* n pole-spacing)))
  109.       (setq n (+ n 2))
  110.       (let ((i fly-row))
  111.         (while (< i floor-row)
  112.           (setq i (1+ i))
  113.           (next-line 1)
  114.           (insert ?\|)
  115.           (delete-char 1)
  116.           (backward-char 1)))))
  117.       ;(sit-for 0)
  118.       ;;
  119.       ;; now draw the rings in their initial positions
  120.       ;;
  121.       (let ((i 0)
  122.         ring)
  123.     (while (< i nrings)
  124.       (setq ring (aref rings (- nrings 1 i)))
  125.       (aset ring 0 (- floor-row i))
  126.       (hanoi-topos (cdr pole-1)
  127.                (- (car pole-1) (- nrings i)))
  128.       (hanoi-draw-ring ring t nil)
  129.       (setcdr pole-1 (1- (cdr pole-1)))
  130.       (setq i (1+ i))))
  131.       (setq buffer-read-only t)
  132.       (sit-for 0)
  133.       ;; Disable display of line and column numbers, for speed.
  134.       (let ((line-number-mode nil)
  135.         (column-number-mode nil))
  136.     ;; do it!
  137.     (hanoi0 (1- nrings) pole-1 pole-2 pole-3))
  138.       (goto-char (point-min))
  139.       (message "Done")
  140.       (setq buffer-read-only t)
  141.       (force-mode-line-update)
  142.       (sit-for 0))))
  143.  
  144. ;;;
  145. ;;; hanoi0 - work horse of hanoi
  146. ;;;
  147. (defun hanoi0 (n from to work)
  148.   (cond ((input-pending-p)
  149.      (signal 'quit (list "I can tell you've had enough")))
  150.     ((< n 0))
  151.     (t
  152.      (hanoi0 (1- n) from work to)
  153.      (hanoi-move-ring n from to)
  154.      (hanoi0 (1- n) work to from))))
  155.  
  156. ;;;
  157. ;;; hanoi-move-ring - move ring 'n' from 'from' to 'to'
  158. ;;;
  159. ;;;
  160. (defun hanoi-move-ring (n from to)
  161.   (let ((ring (aref rings n))        ; ring <- ring: (ring# . row)
  162.     (buffer-read-only nil))
  163.     (let ((row (aref ring 0))        ; row <- row ring is on
  164.       (col (- (car from) n 1))    ; col <- left edge of ring
  165.       (dst-col (- (car to) n 1))    ; dst-col <- dest col for left edge
  166.       (dst-row (cdr to)))        ; dst-row <- dest row for ring
  167.       (hanoi-topos row col)
  168.       (while (> row fly-row)        ; move up to the fly row
  169.     (hanoi-draw-ring ring nil t)    ; blank out ring
  170.     (previous-line 1)        ; move up a line
  171.     (hanoi-draw-ring ring t nil)    ; redraw
  172.     (sit-for 0)
  173.     (setq row (1- row)))
  174.       (setcdr from (1+ (cdr from)))    ; adjust top row
  175.       ;;
  176.       ;; fly the ring over to the right pole
  177.       ;;
  178.       (while (not (equal dst-col col))
  179.     (cond ((> dst-col col)        ; dst-col > col: right shift
  180.            (end-of-line 1)
  181.            (delete-backward-char 2)
  182.            (beginning-of-line 1)
  183.            (insert ?\  ?\  )
  184.            (sit-for 0)
  185.            (setq col (1+ (1+ col))))
  186.           ((< dst-col col)        ; dst-col < col: left shift
  187.            (beginning-of-line 1)
  188.            (delete-char 2)
  189.            (end-of-line 1)
  190.            (insert ?\  ?\  )
  191.            (sit-for 0)
  192.            (setq col (1- (1- col))))))
  193.       ;;
  194.       ;; let the ring float down
  195.       ;;
  196.       (hanoi-topos fly-row dst-col)
  197.       (while (< row dst-row)        ; move down to the dest row
  198.     (hanoi-draw-ring ring nil (> row fly-row)) ; blank out ring
  199.     (next-line 1)            ; move down a line
  200.     (hanoi-draw-ring ring t nil)    ; redraw ring
  201.     (sit-for 0)
  202.     (setq row (1+ row)))
  203.       (aset ring 0 dst-row)
  204.       (setcdr to (1- (cdr to))))))    ; adjust top row
  205.  
  206. ;;;
  207. ;;; draw-ring -    draw the ring at point, leave point unchanged
  208. ;;;
  209. ;;; Input:
  210. ;;;    ring
  211. ;;;    f1    -    flag: t -> draw, nil -> erase
  212. ;;;    f2    -    flag: t -> erasing and need to draw ?\|
  213. ;;;
  214. (defun hanoi-draw-ring (ring f1 f2)
  215.   (save-excursion
  216.     (let* ((string (if f1 (aref ring 1) (aref ring 2)))
  217.        (len (length string)))
  218.       (delete-char len)
  219.       (insert string)
  220.       (if f2
  221.       (progn
  222.         (backward-char (/ (+ len 1) 2))
  223.         (delete-char 1) (insert ?\|))))))
  224.  
  225. (provide 'hanoi)
  226.  
  227. ;;; hanoi.el ends here
  228.